package lumis.portal.user;

import java.util.ArrayList;
import java.util.Collection;

import lumis.portal.AccessDeniedException;
import lumis.portal.PortalEncryptorFactory;
import lumis.portal.PortalException;
import lumis.portal.UnexpectedException;
import lumis.portal.acl.PortalPermissions;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.channel.ChannelConfig;
import lumis.portal.channel.IChannelManager;
import lumis.portal.channel.acl.ChannelPermissions;
import lumis.portal.dao.*;
import lumis.portal.group.IGroupManager;
import lumis.portal.manager.*;
import lumis.portal.principal.PrincipalConfig;
import lumis.portal.serialization.DeserializationContext;
import lumis.util.*;

/**
 * Class responsible for managing operations on the portal users.
 * 
 * @since 4.0.0
 * @version $Revision: 9647 $ $Date: 2008-08-06 16:37:36 -0300 (Wed, 06 Aug 2008) $
 */
public class UserManager implements IUserManager
{
	public void addOrUpdate(SessionConfig sessionConfig, UserConfig userConfig, ITransaction transaction) throws ManagerException, PortalException
	{
		try
		{
			UserConfig userConfigTemp = null;

			try
			{
				userConfigTemp = get(sessionConfig, userConfig.getId(), transaction);
			}
			catch (Exception e)
			{
				userConfigTemp = null;
			}

			if (userConfigTemp != null)
				update(sessionConfig, userConfig, transaction);
			else
				add(sessionConfig, userConfig, transaction);
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	public String add(SessionConfig sessionConfig, UserConfig userConfig, ITransaction transaction) throws ManagerException, PortalException
	{
		try
		{
			if (userConfig.getChannelId() == null)
			{
				if (!ManagerFactory.getPortalAclManager().checkPermission(sessionConfig, PortalPermissions.MANAGE_USER, transaction))
					throw new AccessDeniedException();
			}
			else
			{
				if (!ManagerFactory.getChannelAclManager().checkPermission(sessionConfig, userConfig.getChannelId(), ChannelPermissions.MANAGE_LOCAL_USER, transaction))
					throw new AccessDeniedException();
			}

			if (userConfig.getChannelId() != null)
			{
				IChannelManager channelManager = ManagerFactory.getChannelManager();
				ChannelConfig channelConfig = channelManager.get(sessionConfig, userConfig.getChannelId(), transaction);
				if (!channelConfig.hasLocalGroups())
					throw new AccessDeniedException(); // TODO Ariel: define witch exception is
														// thrown here

				String groupPrefix = channelConfig.getLocalGroupPrefix() + ".";
				if (!userConfig.getLogin().startsWith(groupPrefix))
					userConfig.setLogin(groupPrefix + userConfig.getLogin());
			}

			if (ManagerFactory.getPrincipalManager().getByShortId(sessionConfig, userConfig.getLogin(), transaction) != null)
				throw new ManagerException("STR_PRINCIPAL_ID_ALREADY_EXISTS_USER;" + TextUtil.escapeLocalizationParameter(userConfig.getLogin()));

			if (userConfig.getId() == null)
				userConfig.setId(PortalUtil.generateNewGuid());

			if (userConfig.getPassword() != null)
				userConfig.setPassword(PortalEncryptorFactory.getEncryptor().encrypt(userConfig.getPassword()));

			PrincipalConfig principal = new PrincipalConfig(userConfig);
			ManagerFactory.getPrincipalManager().add(sessionConfig, principal, transaction);

			DaoFactory.getUserDao().add(userConfig, transaction);

			return userConfig.getId();
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	public void update(SessionConfig sessionConfig, UserConfig userConfig, ITransaction transaction) throws ManagerException, PortalException
	{
		if (userConfig.getType() != UserConfig.USER_TYPE_COMMON)
			throw new ManagerException("STR_ERROR_CANT_OPERATE_ON_FRAMEWORK_USERS");
		
		boolean bHasAdminPermission = false;
		boolean bHasPermission = false;

		if(sessionConfig.getUserId().equals(userConfig.getId()))
		{
			bHasPermission = true;
		}
		
		
		if (userConfig.getChannelId() == null)
		{
			if (!ManagerFactory.getPortalAclManager().checkPermission(sessionConfig, PortalPermissions.MANAGE_USER, transaction))
			{
				if(!bHasPermission)
					throw new AccessDeniedException();
			}
			else
			{
				bHasAdminPermission = true;
			}
		}
		else
		{
			if (!ManagerFactory.getChannelAclManager().checkPermission(sessionConfig, userConfig.getChannelId(), ChannelPermissions.MANAGE_LOCAL_USER, transaction))
			{
				if(!bHasPermission)
					throw new AccessDeniedException();
			}
			else
			{
				bHasAdminPermission = true;
			}
		}
		
		// if the user is updating his own information but does not have administration rights,
		// update all properties that only administrators may change with the original values from
		// the database
		if(!bHasAdminPermission)
		{
			UserConfig previousUserConfig = get(sessionConfig, userConfig.getId(), transaction);
			userConfig.setChannelId(previousUserConfig.getChannelId());
			userConfig.setDisabled(previousUserConfig.isDisabled());
			userConfig.setLogin(previousUserConfig.getLogin());
			userConfig.setMultiSession(previousUserConfig.isMultiSession());
			userConfig.setOrigin(previousUserConfig.getOrigin());
			userConfig.setPortalLogin(previousUserConfig.isPortalLogin());
			userConfig.setType(previousUserConfig.getType());
		}

		if (userConfig.getChannelId() != null)
		{
			IChannelManager channelManager = ManagerFactory.getChannelManager();
			ChannelConfig channelConfig = channelManager.get(sessionConfig, userConfig.getChannelId(), transaction);
			if (!channelConfig.hasLocalGroups())
				throw new AccessDeniedException(); // TODO Ariel: define whitch exception is trhown
													// here

			String groupPrefix = channelConfig.getLocalGroupPrefix() + ".";
			if (!userConfig.getLogin().startsWith(groupPrefix))
				userConfig.setLogin(groupPrefix + userConfig.getLogin());
		}

		PrincipalConfig principal = ManagerFactory.getPrincipalManager().getByShortId(sessionConfig, userConfig.getLogin(), transaction);
		if (principal != null && principal.getShortId().equals(userConfig.getLogin()) && !principal.getId().equals(userConfig.getId()))
			throw new ManagerException("STR_PRINCIPAL_ID_ALREADY_EXISTS_USER;" + TextUtil.escapeLocalizationParameter(userConfig.getLogin()));

		DaoFactory.getUserDao().update(userConfig, transaction);
		principal = new PrincipalConfig(userConfig);
		ManagerFactory.getPrincipalManager().update(sessionConfig, principal, transaction);
	}

	public void setPassword(SessionConfig sessionConfig, String userId, String password, ITransaction transaction) throws ManagerException, PortalException
	{
		try
		{
			UserConfig userConfig = get(sessionConfig, userId, transaction);

			String currentUserId = sessionConfig.getUserId();

			if (!currentUserId.equals(userId))
			{
				if (userConfig.getChannelId() == null)
				{
					if (!ManagerFactory.getPortalAclManager().checkPermission(sessionConfig, PortalPermissions.MANAGE_USER, transaction))
						throw new AccessDeniedException();
				}
				else
				{
					if (!ManagerFactory.getChannelAclManager().checkPermission(sessionConfig, userConfig.getChannelId(), ChannelPermissions.MANAGE_CHANNEL_SECURITY, transaction))
						throw new AccessDeniedException();
				}
			}

			if (password.equals(""))
				password = null;

			DaoFactory.getUserDao().setPassword(userId, PortalEncryptorFactory.getEncryptor().encrypt(password), transaction);

		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	public UserConfig get(SessionConfig sessionConfig, String userId, ITransaction transaction) throws ManagerException, PortalException
	{
		return DaoFactory.getUserDao().get(userId, transaction);
	}

	@Deprecated
	public String getUserId(SessionConfig sessionConfig, String userLogin, ITransaction transaction) throws ManagerException, PortalException
	{
		return getUserIdByLogin(sessionConfig, userLogin, transaction);
	}
	
	public String getUserIdByLogin(SessionConfig sessionConfig, String userLogin, ITransaction transaction) throws ManagerException, PortalException
	{
		return DaoFactory.getUserDao().getUserIdByLogin(userLogin, transaction);
	}

	public void delete(SessionConfig sessionConfig, String userId, ITransaction transaction) throws ManagerException, PortalException
	{
		try
		{
			UserConfig userConfig = get(sessionConfig, userId, transaction);

			if (userConfig.getType() != UserConfig.USER_TYPE_COMMON)
				throw new ManagerException("STR_ERROR_CANT_OPERATE_ON_FRAMEWORK_USERS");

			if (userConfig.getChannelId() == null)
			{
				if (!ManagerFactory.getPortalAclManager().checkPermission(sessionConfig, PortalPermissions.MANAGE_USER, transaction))
					throw new AccessDeniedException();
			}
			else
			{
				if (!ManagerFactory.getChannelAclManager().checkPermission(sessionConfig, userConfig.getChannelId(), ChannelPermissions.MANAGE_LOCAL_USER, transaction))
					throw new AccessDeniedException();
			}

			IGroupManager groupManager = ManagerFactory.getGroupManager();

			Collection<String> groups = groupManager.getMemberGroups(sessionConfig, userId, false, transaction);

			for (String group : groups)
			{
				if (!groupManager.get(sessionConfig, group, transaction).hasImpliedMembers())
				{
					try
					{
						groupManager.deleteMember(sessionConfig, group, userId, transaction);
					}
					catch (UnsupportedOperationException e)
					{
						// the group is read-only, so just ignore this exception
					}
				}
			}

			// removes all acl entries associated with this user
			// Calling any one of the ACL managers in this method will do the
			// job of clearing cache for all other managers.
			ManagerFactory.getChannelAclManager().deleteAclEntryByPrincipalId(userId, transaction);

			DaoFactory.getUserDao().delete(userId, transaction);
			ManagerFactory.getPrincipalManager().delete(sessionConfig, userId, transaction);
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	public Collection<UserConfig> getByChannelId(SessionConfig sessionConfig, String channelId, ITransaction transaction) throws ManagerException, PortalException
	{
		try
		{
			Collection<String> userIds = DaoFactory.getUserDao().getByChannelId(channelId, transaction);

			Collection<UserConfig> userConfigs = new ArrayList<UserConfig>(userIds.size());

			for (String userId : userIds)
			{
				userConfigs.add(get(sessionConfig, userId, transaction));
			}

			return userConfigs;
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	public Collection<UserConfig> getByOrigin(SessionConfig sessionConfig, String origin, ITransaction transaction) throws ManagerException, PortalException
	{
		Collection<String> userIds = DaoFactory.getUserDao().getByOrigin(origin, transaction);

		Collection<UserConfig> userConfigs = new ArrayList<UserConfig>(userIds.size());

		for (String userId : userIds)
		{
			userConfigs.add(get(sessionConfig, userId, transaction));
		}

		return userConfigs;
	}

	public void deleteConfig(DeserializationContext deserializationContext, String configId) throws PortalException
	{
		delete(deserializationContext.getSessionConfig(), configId, deserializationContext.getTransaction());
	}

	public void addAlternativeLogin(SessionConfig sessionConfig, String userId, String newLogin, ITransaction transaction) throws PortalException
	{
		// verify permissions
		UserConfig userConfig = get(sessionConfig, userId, transaction);
		if (userConfig.getChannelId() == null)
		{
			if (!ManagerFactory.getPortalAclManager().checkPermission(sessionConfig, PortalPermissions.MANAGE_USER, transaction))
				throw new AccessDeniedException();
		}
		else
		{
			if (!ManagerFactory.getChannelAclManager().checkPermission(sessionConfig, userConfig.getChannelId(), ChannelPermissions.MANAGE_LOCAL_USER, transaction))
				throw new AccessDeniedException();
		}

		// do the addition
		DaoFactory.getUserDao().addAlternativeLogin(userId, newLogin, transaction);
	}

	public Collection<String> getAlternativeLogins(SessionConfig sessionConfig, String userId, ITransaction transaction) throws PortalException
	{
		return DaoFactory.getUserDao().getAlternativeLogins(userId, transaction);
	}

	public void deleteAlternativeLogin(SessionConfig sessionConfig, String loginToDelete, ITransaction transaction) throws PortalException
	{
		// verify permissions
		String userId = getUserIdByLogin(sessionConfig, loginToDelete, transaction);
		UserConfig userConfig = get(sessionConfig, userId, transaction);
		if (userConfig.getChannelId() == null)
		{
			if (!ManagerFactory.getPortalAclManager().checkPermission(sessionConfig, PortalPermissions.MANAGE_USER, transaction))
				throw new AccessDeniedException();
		}
		else
		{
			if (!ManagerFactory.getChannelAclManager().checkPermission(sessionConfig, userConfig.getChannelId(), ChannelPermissions.MANAGE_LOCAL_USER, transaction))
				throw new AccessDeniedException();
		}

		// do the deletion
		DaoFactory.getUserDao().deleteAlternativeLogin(loginToDelete, transaction);
	}
}
